home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 November: Tool Chest / Dev.CD Nov 00 TC Disk 1.toast / Sample Code / Graphics 2D / Restore Screen Cluts / EmergMem.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-09-28  |  9.8 KB  |  284 lines  |  [TEXT/CWIE]

  1. /*
  2.     File:        EmergMem.c
  3.  
  4.     Contains:    Source code for the emergency memory routines
  5.  
  6.     Written by: Forrest Tanaka    
  7.  
  8.     Copyright:    Copyright © 1988-1999 by Apple Computer, Inc., All Rights Reserved.
  9.  
  10.                 You may incorporate this Apple sample source code into your program(s) without
  11.                 restriction. This Apple sample source code has been provided "AS IS" and the
  12.                 responsibility for its operation is yours. You are not permitted to redistribute
  13.                 this Apple sample source code as "Apple sample source code" after having made
  14.                 changes. If you're going to re-distribute the source, we require that you make
  15.                 it clear in the source that the code was descended from Apple sample source
  16.                 code, but that you've made changes.
  17.  
  18.     Change History (most recent first):
  19.                 7/13/1999    Karl Groethe    Updated for Metrowerks Codewarror Pro 2.1
  20.                 
  21.  
  22. */
  23.     
  24.  
  25. /******************************************************************************\
  26. * Header Files
  27. \******************************************************************************/
  28.  
  29. #ifndef THINK_C
  30. #include <OSUtils.h>
  31. #endif
  32.  
  33. #include "EmergMem.h"
  34.  
  35.  
  36. /******************************************************************************\
  37. * Constants
  38. \******************************************************************************/
  39.  
  40. #define kEmergMemSize 32768 /* Number of bytes of emergency mem to allocate */
  41. #define kMemoryMargin 32768 /* “significant amount” amount of free mem */
  42.  
  43.  
  44. /******************************************************************************\
  45. * Global Variables
  46. \******************************************************************************/
  47.  
  48. Handle gEmergMem; /* Handle to block of emergency memory */
  49.  
  50.  
  51. /******************************************************************************\
  52. * Private: AppGrowZone - Custom grow-zone procedure
  53. *
  54. * This is a very basic grow zone procedure.  My application keeps a reserve
  55. * handle of memory in case the Memory Manager gets a request for some memory
  56. * that is not available in my heap.  If memory were to get tight (<32k or so),
  57. * the Toolbox could crash the system.  This grow-zone proc tries to thwart that
  58. * possibility by releasing the 32K block of emergency memory if it hasn’t been
  59. * released already and if the amount of memory requested is less than 32K.
  60. * Hopefully, that’s enough to satisfy the memory request.
  61. *
  62. * There are three conditions in which the emergency memory isn’t freed.  If the
  63. * emergency memory is already free, obviously there isn’t much that can be done.
  64. * If the emergency memory is equal to GZSaveHnd, then it was the reallocation of
  65. * emergency memory that caused this grow-zone proc to be called.  So it doesn’t
  66. * make much sense to free it in that case.  If the size of the memory request is
  67. * more than the size of emergency memory, then I don’t bother to free emergency
  68. * memory because I assume that the toolbox handles such huge requests for memory
  69. * properly.  Warning: that isn’t always a good assumption, but that’s not my
  70. * fault.
  71. *
  72. *     WARNING: Register A5 might not be valid when grow-zone procedures
  73. *     are called. Read Technical Note #136 and 208.
  74. *
  75. * The "cbNeeded" parameter is the number of bytes that the Memory Manager needs
  76. * to fulfill the memory request it had received.  The number of bytes actually
  77. * freed by AppGrowZone is returned.
  78. \******************************************************************************/
  79.  
  80. static pascal long AppGrowZone(
  81.     Size cbNeeded) /* Number of bytes needed by Memory Manager */
  82. {
  83.     long theA5;       /* Value of A5 when AppGrowZone is called */
  84.     long amountFreed; /* Number of bytes freed up */
  85.  
  86.     /* Remember the current value of A5 */
  87.     theA5 = SetCurrentA5();
  88.  
  89.     /* Free emergency memory if possible */
  90.     if (!NoEmergMem() && (gEmergMem != GZSaveHnd()) &&
  91.             (cbNeeded <= kEmergMemSize))
  92.     {
  93.         EmptyHandle( gEmergMem );
  94.         amountFreed = kEmergMemSize;
  95.     }
  96.     else
  97.         amountFreed = 0;
  98.  
  99.     /* Restore A5 */
  100.     (void)SetA5( theA5 );
  101.  
  102.     /* Return number of bytes freed */
  103.     return amountFreed;
  104. }
  105.  
  106.  
  107. /******************************************************************************\
  108. * Public: InstallAppGZ
  109. *
  110. * Installing our custom grow-zone procedure simply involves calling SetGrowZone
  111. * with the address of our custom grow-zone procedure.
  112. \******************************************************************************/
  113.  
  114. void InstallAppGZ()
  115. {
  116.     SetGrowZone( NewGrowZoneProc(AppGrowZone) );
  117. }
  118.  
  119.  
  120. /******************************************************************************\
  121. * Public: DeinstallAppGZ
  122. *
  123. * Passing NIL to SetGrowZone is all that’s needed to tell the Memory Manager not
  124. * to call a grow-zone procedure whenenver memory requests can’t be satisfied.
  125. \******************************************************************************/
  126.  
  127. void DeinstallAppGZ()
  128. {
  129.     SetGrowZone( nil );
  130. }
  131.  
  132.  
  133. /******************************************************************************\
  134. * Public: InitEmergMem
  135. *
  136. * If the block of emergency memory couldn’t be allocated, then we’re probably in
  137. * some pretty big trouble.  But InitEmergMem tries to deal as best it can in
  138. * that case by allocating an empty handle; that is, it allocates a master
  139. * pointer that’s set to NIL.  That leaves it up to the rest of the application
  140. * to decide what to do.
  141. \******************************************************************************/
  142.  
  143. void InitEmergMem()
  144. {
  145.     /* Allocate the block of emergency memory */
  146.     gEmergMem = NewHandle( kEmergMemSize );
  147.     if (gEmergMem == nil)
  148.         /* Couldn’t allocate emergency memory; just allocate an empty handle */
  149.         gEmergMem = NewEmptyHandle();
  150.  
  151.     /* Now that emergency memory is initialized, can install grow zone proc */
  152.     InstallAppGZ();
  153. }
  154.  
  155.  
  156. /******************************************************************************\
  157. * Public: RecoverEmergMem
  158. *
  159. * ReallocHandle takes an existing empty handle (handle whose master pointer is
  160. * nil) and allocates a block of memory for it.  Perfect for this job!
  161. \******************************************************************************/
  162.  
  163. void RecoverEmergMem()
  164. {
  165.     ReallocateHandle( gEmergMem, kEmergMemSize );
  166. }
  167.  
  168.  
  169. /******************************************************************************\
  170. * Public: FailLowMemory
  171. *
  172. * PurgeSpace is used to determine how much free memory there’d be in the heap if
  173. * all purgeable blocks were purged.  If this amount is less than the amount
  174. * needed, or if there isn’t any emergency memory, true is returned.
  175. \******************************************************************************/
  176.  
  177. Boolean FailLowMemory(
  178.     long memRequest) /* Amount of memory to check */
  179. {
  180.     long total;  /* Total amount of free memory if heap was purged */
  181.     long contig; /* Max amount of free contiguous memory if heap was purged */
  182.  
  183.     PurgeSpace( /*<*/&total, /*<*/&contig );
  184.     return (total < (memRequest + kMemoryMargin)) || NoEmergMem();
  185. }
  186.  
  187.  
  188. /******************************************************************************\
  189. * Public: NoEmergMem
  190. *
  191. * We check on the handle and the master pointer of gEmergMem to see if the
  192. * emergency memory block has been emptied by AppGrowZone, or was never allocated
  193. * in the first place.  StripAddress is called because we’re comparing the master
  194. * pointer of the emergency memory handle against zero, and the upper byte of
  195. * master pointers can be non-zero if the machine is booted in 24-bit addressing
  196. * mode.
  197. \******************************************************************************/
  198.  
  199. Boolean NoEmergMem()
  200. {
  201.     /* Empty handle means no emergency memory */
  202.     return (gEmergMem == nil) || (StripAddress( *gEmergMem ) == nil);
  203. }
  204.  
  205.  
  206. /******************************************************************************\
  207. * Public: NewHandleMargin
  208. *
  209. * I don’t call SysError with an ID 25 if there isn’t enough memory to satisfy
  210. * the request, so there isn’t much reason to use the grow-zone proc.  So, I
  211. * deinstall the grow-zone proc temporarily just before I allocate the memory.
  212. \******************************************************************************/
  213.  
  214. Handle NewHandleMargin(
  215.     Size    requestedSize, /* Number of bytes requested to be allocated */
  216.     Boolean appHeapAlloc,  /* Allocate in app heap or system heap? */
  217.     Boolean clearMem)      /* Clear allocated memory or leave it alone? */
  218. {
  219.     Handle aHandle; /* Handle to newly-allocated memory */
  220.  
  221.     if (FailLowMemory( requestedSize ))
  222.         aHandle = nil;
  223.     else
  224.     {
  225.         /* We handle memFullErr properly, so don’t need grow-zone proc */
  226.         DeinstallAppGZ();
  227.  
  228.         /* Allocate the memory with the requested options */
  229.         if (!appHeapAlloc && clearMem)
  230.             aHandle = NewHandleSysClear( requestedSize );
  231.         else if (!appHeapAlloc)
  232.             aHandle = NewHandleSys( requestedSize );
  233.         else if (clearMem)
  234.             aHandle = NewHandleClear( requestedSize );
  235.         else
  236.             aHandle = NewHandle( requestedSize );
  237.  
  238.         /* Install the grow-zone proc again */
  239.         InstallAppGZ();
  240.     }
  241.  
  242.     return aHandle;
  243. }
  244.  
  245.  
  246. /******************************************************************************\
  247. * Public: NewPtrMargin
  248. *
  249. * I don’t call SysError with an ID 25 if there isn’t enough memory to satisfy
  250. * the request, so there isn’t much reason to use the grow-zone proc.  So, I
  251. * disconnect the grow-zone proc temporarily just before I allocate the memory.
  252. \******************************************************************************/
  253.  
  254. Ptr NewPtrMargin(
  255.     Size    requestedSize, /* Number of bytes requested to be allocated */
  256.     Boolean appHeapAlloc,  /* Allocate in app heap or system heap? */
  257.     Boolean clearMem)      /* Clear allocated memory or leave it alone? */
  258. {
  259.     Ptr aPtr; /* Pointer to newly-allocated memory */
  260.  
  261.     if (FailLowMemory( requestedSize ))
  262.         aPtr = nil;
  263.     else
  264.     {
  265.         /* We handle memFullErr properly, so don’t need grow-zone proc */
  266.         DeinstallAppGZ();
  267.  
  268.         /* Allocate the memory with the requested options */
  269.         if (!appHeapAlloc && clearMem)
  270.             aPtr = NewPtrSysClear( requestedSize );
  271.         else if (! appHeapAlloc)
  272.             aPtr = NewPtrSys( requestedSize );
  273.         else if (clearMem)
  274.             aPtr = NewPtrClear( requestedSize );
  275.         else
  276.             aPtr = NewPtr( requestedSize );
  277.  
  278.         /* Connect up the grow-zone proc again */
  279.         InstallAppGZ();
  280.     }
  281.  
  282.     return aPtr;
  283. }
  284.